#pragma once

#include <string>
#include <vector>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <sstream>
#include <fstream>
#include "Log.hpp"
#include <functional>

static const std::string sep = "\r\n";             // 行分隔符
static const std::string header_sep = ": ";        // 报头分隔符
static const std::string wwwroot = "./wwwroot";    // wwwroot目录
static const std::string homepage = "index.html";  // 默认主页
static const std::string httpversion = "HTTP/1.1"; // HTTP版本
static const std::string space = " ";              // 空格
static const std::string filesuffixsep = ".";      // 文件分隔符
static const std::string args_sep = "?";           // 参数分隔符

class HttpRequest;
class HttpResponse;

using func_t = std::function<std::shared_ptr<HttpResponse>(std::shared_ptr<HttpRequest>)>; // 回调函数类型

// http内部，要根据目标要访问的 资源的文件后缀，区分清楚文件类型，同时Content-Type告诉浏览器，我的response的正文的 类型！
class HttpRequest
{
private:
    // 获取一行
    std::string GetOneline(std::string &reqstr)
    {
        if (reqstr.empty())
            return reqstr;           // 如果为空，则返回空
        auto pos = reqstr.find(sep); // 找到第一个行分隔符
        if (pos == std::string::npos)
            return std::string(); // 如果没有找到匹配项，则返回空

        std::string line = reqstr.substr(0, pos); // 截取从0到pos的字符串
        reqstr.erase(0, pos + sep.size());        // 删除从0到pos+sep.size()的字符串

        return line.empty() ? sep : line; // 返回截取的字符串,如果为空，则返回行分隔符
    }

    bool ParseHeaderHelper(const std::string &line, std::string *k, std::string *v)
    {
        auto pos = line.find(header_sep); // 找到第一个报头分隔符
        if (pos == std::string::npos)
            return false; // 如果没有找到匹配项，则返回false

        *k = line.substr(0, pos);                  // 截取从0到pos的字符串
        *v = line.substr(pos + header_sep.size()); // 截取从pos+header_sep.size()到末尾的字符串

        return true;
    }

public:
    HttpRequest() : _blank_line(sep), _path(wwwroot)
    {
    }
    ~HttpRequest()
    {
    }
    // 序列化
    void Serialize()
    {
    }
    // 反序列化
    void Deserialize(std::string &reqstr) // reqstr是客户端发来的请求
    {
        _req_line = GetOneline(reqstr); // 获取请求行
        while (1)
        {
            std::string line = GetOneline(reqstr); // 获取一行
            if (line.empty())
                break;            // 如果为空，则跳出循环
            else if (line == sep) // 如果是行分隔符，则下面是text
            {
                _req_text = reqstr; // 获取请求正文
            }
            else
            {
                _req_header.emplace_back(line); // 将请求报头添加到vector中
            }
        }
        ParseReqLine(); // 解析请求行
        ParseHeader();  // 解析请求报头
    }

    bool ParseReqLine()
    {
        // 解析请求行
        if (_req_line.empty())
            return false;                  // 如果为空，则返回false
        std::stringstream ss(_req_line);   // 将请求行转换为流
        ss >> _method >> _url >> _version; // 从流中读取请求方法、请求路径和版本

        // /index.html?use=zhangsan&passwd=123456
        if (strcasecmp("get", _method.c_str()) == 0) // 如果请求方法为GET   忽略大小写
        {
            auto pos = _url.find(args_sep); // 找到第一个参数分隔符
            if (pos != std::string::npos)
            {
                LOG(INFO, "change begin,url: %s\n", _url.c_str());               // 打印请求路径
                _args = _url.substr(pos + args_sep.size());                      // 如果是GET方法，则截取参数部分
                _url.resize(pos);                                                // 缩容到？位置
                LOG(INFO, "change done,url: %s\n", _url.c_str(), _args.c_str()); // 打印请求路径和参数
            }
        }

        _path += _url; // 将请求路径添加到wwwroot目录中

        LOG(DEBUG, "url: %s\n", _url.c_str()); // 打印请求路径

        // 判断一下是不是请求的/ -- 如果是wwwroot/  那就访问默认的主页
        if (_path[_path.size() - 1] == '/')
        {
            _path += homepage; // 如果是，则添加默认主页
        }

        auto pos = _path.rfind(filesuffixsep); // 从后往前找到文件后缀
        if (pos == std::string::npos)
        {
            _suffix = ".unknown"; // 如果没有找到匹配项，则默认为.html
        }
        else
        {
            _suffix = _path.substr(pos); // 截取文件后缀
        }

        LOG(INFO, "client wang get %s,_suffix: %s\n", _path.c_str(), _suffix.c_str()); // 打印请求路径

        return true;
    }
    bool ParseHeader()
    {
        for (auto &header : _req_header)
        {
            std::string k, v;
            if (ParseHeaderHelper(header, &k, &v)) // 解析请求报头
            {
                _headers.insert(std::make_pair(k, v)); // 将请求报头键值对添加到unordered_map中
            }
        }
        return true;
    }

    std::string Path()
    {
        return _path; // 返回资源路径
    }
    std::string Suffix()
    {
        return _suffix; // 返回文件后缀
    }
    bool IsExec()
    {
        return !_args.empty() || !_req_text.empty(); // 如果有参数或正文，则返回true
    }
    std::string Args()
    {
        return _args; // 返回参数
    }
    std::string Text()
    {
        return _req_text; // 返回正文
    }
    std::string Method()
    {
        return _method; // 返回请求方法
    }

    void Print()
    {
        std::cout << "=======" << _req_line << std::endl; // 打印请求行
        for (auto &header : _req_header)                  // 打印请求报头
        {
            std::cout << "******" << header << std::endl;
        }
        std::cout << _blank_line << std::endl; // 打印空行
        std::cout << _req_text << std::endl;   // 打印请求正文

        std::cout << "method ### " << _method << std::endl;   // 打印请求方法
        std::cout << "url ### " << _url << std::endl;         // 打印请求路径
        std::cout << "path ### " << _path << std::endl;       // 打印资源路径
        std::cout << "version ### " << _version << std::endl; // 打印版本
        for (auto &header : _headers)                         // 打印请求报头键值对
        {
            std::cout << "@@@" << header.first << " -- " << header.second << std::endl;
        }
    }

private:
    std::string _req_line;                // GET / HTTP/1.1 请求行
    std::vector<std::string> _req_header; // 请求报头
    std::string _blank_line;              // 空行
    std::string _req_text;                // 请求正文

    // 期望的解析结果
    std::string _method;  // 请求方法
    std::string _url;     // 请求路径
    std::string _path;    // 资源路径
    std::string _version; // 版本
    std::string _suffix;  // 文件后缀
    std::string _args;    // GET参数

    std::unordered_map<std::string, std::string> _headers; // 响应报头键值对
};

class HttpResponse
{
public:
    HttpResponse() : _version(httpversion), _blank_line(sep)
    {
    }
    ~HttpResponse() {}

    // 添加状态码
    void AddStatusLine(int code, const std::string &desc)
    {
        _code = code; // 设置状态码
        _desc = desc; // 根据状态码设置状态码描述
    }

    void AddHeader(const std::string &k, const std::string &v)
    {
        LOG(DEBUG, "AddHeader: %s->%s\n", k.c_str(), v.c_str()); // 打印响应报头键值对
        _headers[k] = v;                                         // 将响应报头键值对添加到unordered_map中
    }

    void AddText(const std::string &text)
    {
        _rseq_text = text; // 将响应正文添加到响应报文中
    }

    // 序列化
    std::string Serialize()
    {
        std::string _status_line = _version + space + std::to_string(_code) + space + _desc + sep; // 构建状态行
        for (auto &header : _headers)
        {
            _resq_headers.emplace_back(header.first + header_sep + header.second + sep); // 构建响应报头
        }
        // 序列化
        std::string respstr = _status_line; // 状态行
        for (auto &header : _resq_headers)
        {
            respstr += header; // 响应报头
        }
        respstr += _blank_line; // 空行
        respstr += _rseq_text;  // 响应正文
        return respstr;
    }

private:
    // 构建应答的必要字段
    std::string _version; // 版本
    int _code;            // 状态码
    std::string _desc;    // 状态码描述

    std::unordered_map<std::string, std::string> _headers; // 响应报头键值对

    // 应答的结构化字段  原始协议内容
    std::string _status_line;               // GET / HTTP/1.1 状态行
    std::vector<std::string> _resq_headers; // 响应报头
    std::string _blank_line;                // 空行
    std::string _rseq_text;                 // 响应正文
};

// 工厂
class Factory
{

public:
    // 工厂方法
    static std::shared_ptr<HttpRequest> BuildHttpRequest()
    {
        return std::make_shared<HttpRequest>(); // 返回HttpRequest对象
    }

    static std::shared_ptr<HttpResponse> BuildHttpResponse()
    {
        return std::make_shared<HttpResponse>(); // 返回HttpResponse对象
    }
};

class HttpServer
{
public:
    HttpServer()
    {
        // 报头对照表
        _mime_type.insert(std::make_pair(".html", "text/html"));
        _mime_type.insert(std::make_pair(".css", "text/css"));
        _mime_type.insert(std::make_pair(".js", "text/javascript"));
        _mime_type.insert(std::make_pair(".jpg", "image/jpeg"));
        _mime_type.insert(std::make_pair(".unknown", "text/html"));

        // 状态码对照表
        _code_to_desc.insert(std::make_pair(100, "Continue"));
        _code_to_desc.insert(std::make_pair(200, "OK"));
        _code_to_desc.insert(std::make_pair(301, "Moved Permanently"));
        _code_to_desc.insert(std::make_pair(302, "Found"));
        _code_to_desc.insert(std::make_pair(404, "Not Found"));
        _code_to_desc.insert(std::make_pair(500, "Internal Server Error"));
    }
    ~HttpServer()
    {
    }

    void AddHandler(const std::string functionname, func_t f)
    {
        std::string key = wwwroot + functionname; // wwwroot + functionname(/login)
        _funcs[functionname] = f;                 //
    }

    std::string ReadFileContent(const std::string &path, int *size)
    {
        // 要按二进制方式打开
        std::ifstream in(path, std::ios::binary); // 以二进制方式打开
        if (!in.is_open())                        // 打开失败
        {
            return std::string(); // 返回空字符串
        }
        in.seekg(0, in.end);       // 文件偏移量设置到结尾
        int filesize = in.tellg(); // 获取文件大小
        in.seekg(0, in.beg);       // 文件偏移量设置到开头

        std::string content;
        content.resize(filesize);                   // 设置字符串大小
        in.read((char *)content.c_str(), filesize); // 读取文件内容到字符串
        in.close();                                 // 关闭文件
        *size = filesize;                           // 文件大小
        return content;                             // 返回文件内容
    }
    // #define TEST 1;
    std::string HandlerHttpReqest(std::string req)
    {
#ifdef TEST
        std::cout << "---------------------------------------" << std::endl;
        std::cout << req << std::endl;

        std::string response = "HTTP/1.0 200 OK\r\n"; //
        response += "\r\n";
        response += "<html><body><h1>hello world,hhhhhhhhh jjjj</h1></body></html>";

        return response;
#else
        std::cout << "---------------------------------------" << std::endl;
        std::cout << req << std::endl;

        std::shared_ptr<HttpRequest> request = Factory::BuildHttpRequest();
        request->Deserialize(req); // 反序列化

        // std::string newurl = "https://www.csdn.net/"; // 重定向地址
        // std::string newurl = "https://112.74.189.8:8889/3.html"; // 重定向地址
        // int code = 0;
        // if (request->Path() == "./wwwroot/redir")
        // {
        //     code = 301;
        //     response->AddStatusLine(code, _code_to_desc[code]); // 添加状态码
        //     response->AddHeader("Location", newurl);            // 添加响应报头
        // }

        if (request->IsExec())//是否有正文或参数
        {
            //先预先在_funcs中注册路径和路径的处理方法，当请求到达之后，通过路径获取对应的处理函数，然后直接调用:传递request处理
            auto response = _funcs[request->Path()](request); // 调用函数
            return response->Serialize();                     // 序列化
        }
        else
        {
            auto response = Factory::BuildHttpResponse(); // 创建响应对象
            int code = 200;
            int contensize = 0;

            std::string text = ReadFileContent(request->Path(), &contensize); // 读取文件内容
            if (text.empty())
            {
                code = 404;
                response->AddStatusLine(code, _code_to_desc[code]);                     // 添加状态码
                std::string text404 = ReadFileContent("wwwroot/404.html", &contensize); // 读取文件内容
                response->AddHeader("Content-Length", std::to_string(contensize));      // 添加响应报头
                response->AddHeader("Content-Type", _mime_type[".html"]);               // 添加响应报头
                response->AddText(text404);                                             // 添加响应正文
            }
            else
            {
                std::string suffix = request->Suffix();                            // 获取文件后缀
                response->AddStatusLine(code, _code_to_desc[code]);                // 添加状态码
                response->AddHeader("Content-Length", std::to_string(contensize)); // 添加响应报头
                response->AddText(text);                                           // 添加响应正文
                response->AddHeader("Content-Type", _mime_type[suffix]);           // 添加响应报头
            }
            return response->Serialize(); // 序列化
        }

        // resonse->AddHeader("Content-Length", std::to_string(contensize)); // 添加响应报头
        // http协议已经给我们规定好了不同文件后缀对应的Content-Type
        // response->AddHeader("Content-Type", _mime_type[suffix]);  // 添加响应报头
        // response->AddHeader("Location", "https://www.csdn.net/"); // 添加响应报头
        // resonse->AddText(text);                                 // 添加响应正文

#endif
    }

private:
    std::unordered_map<std::string, std::string> _mime_type; // 文件后缀和Content-Type的映射表
    std::unordered_map<int, std::string> _code_to_desc;      // 状态码和状态描述的映射表
    std::unordered_map<std::string, func_t> _funcs;// 路径和方法
};